import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

@FunctionalInterface
interface V2Transformation {
    public float[] compute(float[] input);
}

interface V2Transformation2 {
    public float[] compute(float[] input, Object transArg);
}

public class LambdaDemo {
    // https://objcoding.com/2019/03/04/lambda/#lambda-and-anonymous-classesi
    // https://www.jianshu.com/p/f02cdc710608

    // 并不需要额外的工作来声明一个接口是函数式接口：编译器会根据接口的结构自行判断
    // 判断过程并非简单的对接口方法计数：一个接口可能冗余的定义了一个Object已经提供的方法，比如toString()，或者定义了静态方法或默认方法，这些都不属于函数式接口方法的范畴）。
    // 不过API作者们可以通过 @FunctionalInterface 注解来显式指定一个接口是函数式接口（以避免无意声明了一个符合函数式标准的接口），加上这个注解之后，编译器就会验证该接口是否满足函数式接口的要求。
    //
    // Lambda表达式必须对应一个函数式接口，方法体其实就是函数接口的实现。
    // 编译器利用Lambda表达式所在上下文所期待的类型进行推导，这个被期待的类型被称为目标类型。Lambda表达式只能出现在目标类型为函数式接口的上下文中。

    // 匿名内部类仍然是一个类，只是不需要程序员显示指定类名，编译器会自动为该类取名，编译之后将会多产生1个class文件
    // Lambda表达式不会产生新的类, Lambda表达式中this的意义跟在表达式外部完全一样

    public static String formatV2(float[] input) {
        if (input.length < 2) {
            return "not_V2";
        }
        return "("+input[0]+", "+input[1]+")";
    }

    public static float[] v2Compute(float[] input, V2Transformation trans) {
        if (input.length < 2) {
            return input;
        }
        return trans.compute(input);
    }

    public static float[] v2Compute2(float[] input, Object tArg, V2Transformation2 trans) {
        if (input.length < 2) {
            return input;
        }
        return trans.compute(input, tArg);
    }

    protected static void v2ComputeByLambda() {
        float input[] = {1.0f, 2.0f};
        // 笛卡尔坐标 转 极坐标
        float[] polarCoord = v2Compute(input, (in)->{
            float rtn[] = new float[2];
            // 极坐标 r
            rtn[0] = (float)(Math.sqrt(in[0]*in[0] + in[1]*in[1]));
            if (0 == rtn[0]) {
                rtn[1] = 0;
            } else {
                // 极坐标 角度 1 2 象限
                rtn[1] = (float)(Math.acos(in[0]/rtn[0]));
                if (in[0] < 0) {
                    // 3 4 象限
                    rtn[1] = (float)(Math.PI - rtn[1]);
                }
            }
            return rtn;
        });
        System.out.println("笛卡尔坐标 "+formatV2(input)+" 转 极坐标 "+ formatV2(polarCoord));


        // 笛卡尔坐标 缩放
        float sx = (float)(Math.PI);
        float sy = (float)(Math.E);
        float[] scaleCoord = v2Compute(input, (in)->{
            float rtn[] = new float[2];
            rtn[0] = in[0] * sx; // 变量捕获
            rtn[1] = in[1] * sy; // 变量捕获
            return rtn;
        });
        System.out.println("笛卡尔坐标 "+formatV2(input)+" 缩放 "+ formatV2(scaleCoord));

        // 笛卡尔坐标 旋转
        for (double angle = Math.PI/6; angle < Math.PI; angle += Math.PI/6) {
            float[] angleCoord = v2Compute2(input, angle, (in, angleInput) -> {
                float rtn[] = new float[2];
                double angleFromInput= ((Double)(angleInput)).doubleValue();
                rtn[0] = (float)(in[0] * Math.cos(angleFromInput) + in[1] * Math.sin(angleFromInput)); // 变量捕获 外部的 angle编译不过
                rtn[1] = (float)(in[1] * Math.cos(angleFromInput) - in[0] * Math.sin(angleFromInput)); // angle 只能当做参数传递进来
                return rtn;
            });
            System.out.println("笛卡尔坐标 " + formatV2(input) + " 旋转 angle="+angle+" -> "+ formatV2(angleCoord));
        }
    }

    protected static void dicSortByLambda() {
        HashMap<String, Integer> monthDic = new HashMap<String, Integer>() {
            // 第一层括弧实际是定义了一个匿名内部类 (Anonymous Inner Class)
            {
                // 第二层括弧实际上是一个实例初始化块 (instance initializer block)，这个块在内部匿名类构造时被执行

                put("January", 31);
                put("February", 28);
                put("March", 31);
                put("April", 30);
                put("May", 31);
                put("June", 30);
                put("July", 31);
                put("August", 31);
                put("September", 30);
                put("October", 31);
                put("November", 30);
                put("December", 31);
            }
        };

        // 字典遍历 by lambda
        monthDic.forEach((key, val) -> {System.out.println("foreach loop, key:" + key + ",\t vaule:"+val);});

        // 字典key 排序 by 静态方法引用
        List<String> keyList = new ArrayList<String>(monthDic.keySet());
        Collections.sort(keyList, String::compareTo); // 静态方法引用, 在需要写 lambda 的地方使用已有的 类静态方法
        keyList.forEach((key) -> {System.out.println("defaultSort, key:" + key + ",\t vaule:"+monthDic.get(key));});

        // 字典key 排序 by lambda
        Collections.sort(keyList, (val1, val2) -> {
            // 按第二个字母升序排序
            // 返回1 表示 val1 按升序 排在 val2 后面
            return val1.charAt(1) > val2.charAt(1) ? 1 : -1;
        });
        keyList.forEach((key) -> {System.out.println("第二个字母升序, key:" + key + ",\t vaule:"+monthDic.get(key));});

        // replaceAll, 用 lambda 的执行结果替换原来的value
        monthDic.replaceAll((k, v) -> v.intValue()%2); // 只有一条语句，省略{}
        monthDic.forEach((key, val) -> {System.out.println("after replace, key:" + key + ",\t is_odd_daynum:"+val);});

    }

    protected static void demoLogic() {
        DateTimeDemo.logTs("dicSortByLambda");
        dicSortByLambda();

        DateTimeDemo.logTs("v2ComputeByLambda");
        v2ComputeByLambda();

    }
}
